//
//	HOME AUTOMATION GATEWAY PROJECT
//
//  (c) 2008 mocom software GmbH & Co KG
//	for European Microsoft Innovation Center
//
//  $Author: volker $
//  $Date: 2009-09-29 13:26:19 +0200 (Di, 29. Sep 2009) $
//  $Revision: 503 $
//
//  Microsoft dotNetMF Project
//  Copyright ©2001,2002,2003,2004 Microsoft Corporation
//  One Microsoft Way, Redmond, Washington 98052-6399 U.S.A.
//  All rights reserved.
//  MICROSOFT CONFIDENTIAL
//
//-----------------------------------------------------------------------------

#include <tinyhal.h>
#include "DD160128FC2A.h"

//////////////////////////////////////////////////////////////////////////////

#undef  TRACE_ALWAYS
#define TRACE_ALWAYS               0x00000001

#undef  DEBUG_TRACE
#define DEBUG_TRACE (TRACE_ALWAYS)

#undef DEBUG
#define DEBUG	0

#if DEBUG
#define PRINT_VAR(x)	hal_printf("LCD: %s=%8.8lx [%c]\r\n",#x,x,((x) < 0x20 || (x) > (int)'z') ? '.' : (char)x)
#define PRINT_MSG(x)	hal_printf("LCD: %s\r\n",x)
#define PRINT_HAL(x)	hal_term(x)
#else
#define PRINT_VAR(x)
#define PRINT_MSG(x)
#define PRINT_HAL(x)
#endif

#define DRIVE_DISPLAY	1

//////////////////////////////////////////////////////////////////////////////

#if defined(ADS_LINKER_BUG__NOT_ALL_UNUSED_VARIABLES_ARE_REMOVED)
#pragma arm section zidata = "g_DD160128FC2A_Driver"
#endif

DD160128FC2A_Driver g_DD160128FC2A_Driver;

#if defined(ADS_LINKER_BUG__NOT_ALL_UNUSED_VARIABLES_ARE_REMOVED)
#pragma arm section zidata
#endif

static UINT32 lcd_buffer[160*128];

///// CONFIG FOR NOW
#define HAG_CONFIG_GPIO_PIN_RS		6
#define HAG_CONFIG_OLED_SPI_CS		AT91_GPIO_Driver::PA5
#define HAG_CONFIG_OLED_SPI_MOD		0

//////////////////////////////////////////////////////////////////////////////
//
//  HAL TERM
//
void hal_term(char x)
{
	static int first = 0;

	if (x == '\n' || first == 0) {
		hal_printf("\r\nHT: ");
		first = 1;
	} else {
		hal_printf("%c",x);
	}
}

//////////////////////////////////////////////////////////////////////////////
//
//  Pixel-IF
//
void DD160128FC2A_Driver::SendPixel(UINT16 *data, unsigned pixel)
{
#if DRIVE_DISPLAY
	UINT16 trash;

	SPI_XACTION_16 xAction =
    {
		data,
        pixel,
		&trash,
        (pixel) ? 0 : 1,
        0,
        g_DD160128FC2A_Driver.SpiConfig.SPI_mod
	};

	// send data.
	g_DD160128FC2A_Driver.SpiConfig.MD_16bits = 1;			// 16 bit transfers

	CPU_SPI_Xaction_Start(g_DD160128FC2A_Driver.SpiConfig);
    CPU_SPI_Xaction_nWrite16_nRead16(xAction);
	CPU_SPI_Xaction_Stop (g_DD160128FC2A_Driver.SpiConfig);
#endif
}

void DD160128FC2A_Driver::SetPixelPos(UINT16 x, UINT16 y)
{
	if (x < g_DD160128FC2A_Driver.c_columns)
		SPI_Write_Command_Data(0x20,x);

	if (y < g_DD160128FC2A_Driver.c_rows)
		SPI_Write_Command_Data(0x21,y);

	// start data write opperation
	SPI_Write_Command(0x22);
}

void DD160128FC2A_Driver::SetPixel(UINT16 x, UINT16 y, UINT16 *data)
{
	SetPixelPos(x,y);
	SendPixel(data,1);
}

//////////////////////////////////////////////////////////////////////////////

UINT32 *DD160128FC2A_Driver::GetFrameBuffer()
{
	return lcd_buffer; //LCD_BUFFER_BASE;
}

//--//

UINT8 DD160128FC2A_Driver::SPI_Write_Data(UINT8 data)
{
#if DRIVE_DISPLAY
	UINT8 readback = 0x55;

	SPI_XACTION_8 xAction =
    {
		&data,
        1,
        &readback,
        1,
        0,
        g_DD160128FC2A_Driver.SpiConfig.SPI_mod
	};

	g_DD160128FC2A_Driver.SpiConfig.MD_16bits = 0;			// 8 bit transfers

    CPU_SPI_Xaction_Start(g_DD160128FC2A_Driver.SpiConfig);
    CPU_SPI_Xaction_nWrite8_nRead8(xAction);
    CPU_SPI_Xaction_Stop (g_DD160128FC2A_Driver.SpiConfig);
	return readback;
#else
	return data;
#endif
}

void DD160128FC2A_Driver::SPI_Write_Command(UINT8 cmd)
{
	//PRINT_MSG("SPI_Write_Command");
	//PRINT_VAR(cmd);
	// set Port "RS" low
	CPU_GPIO_SetPinState(HAG_CONFIG_GPIO_PIN_RS,0);
	// send command
	SPI_Write_Data(cmd);
	// set Port "RS" high
	CPU_GPIO_SetPinState(HAG_CONFIG_GPIO_PIN_RS,1);
}

UINT8 DD160128FC2A_Driver::SPI_Write_Command_Data(UINT8 cmd, UINT8 data)
{
	SPI_Write_Command(cmd);
	return SPI_Write_Data(data);
}


BOOL DD160128FC2A_Driver::Initialize()
{
	//PRINT_MSG("INIT DISPLAY OLED_DD160128FC2A");
	UINT8	sr;

#if DRIVE_DISPLAY
	PRINT_MSG("DISPLAY OLED_DD160128FC2A ENABLED");
#else
	PRINT_MSG("DISPLAY OLED_DD160128FC2A STUB, NO SPI OUTPUT");
#endif

	// setup RS pin, Output LOW
	CPU_GPIO_EnableOutputPin(HAG_CONFIG_GPIO_PIN_RS,1);

	// setup SPI, DeviceCS and SPI_mod are the only config arguments
	g_DD160128FC2A_Driver.SpiConfig.DeviceCS 	   = HAG_CONFIG_OLED_SPI_CS;
    g_DD160128FC2A_Driver.SpiConfig.SPI_mod  	   = HAG_CONFIG_OLED_SPI_MOD;

	// std
	g_DD160128FC2A_Driver.SpiConfig.CS_Active 	   = 0;			// not used, HW CS
    g_DD160128FC2A_Driver.SpiConfig.MSK_IDLE	   = TRUE;		// False = LOW during idle, TRUE = HIGH during idle
    g_DD160128FC2A_Driver.SpiConfig.MSK_SampleEdge = TRUE;      // False = sample falling edge,  TRUE = samples on rising
    g_DD160128FC2A_Driver.SpiConfig.Clock_RateKHz  = 16000;		// 16MHz
    g_DD160128FC2A_Driver.SpiConfig.CS_Setup_uSecs = 0;
    g_DD160128FC2A_Driver.SpiConfig.CS_Hold_uSecs  = 0;

	sr = SPI_Write_Command_Data(0x01,0x00);

	// init
	SPI_Write_Command_Data(0x06,0x00);    //Set Display On_Off (display off)
	SPI_Write_Command_Data(0x05,0x00);    //Set SOFT_RST (normal mode)
    SPI_Write_Command_Data(0x04,0x03);    //Set REDUCE_CURRENT (disp off analog RST)
    //Time_Sleep_MicroSeconds (10*1000);
    SPI_Write_Command_Data(0x04,0x00);    //(disp off, normal)
    //Time_Sleep_MicroSeconds (10*1000);
    SPI_Write_Command_Data(0x3B,0x00);
    SPI_Write_Command_Data(0x02,0x41);    //Set OSC_CTL (internal OSC on)
    SPI_Write_Command_Data(0x03,0x30);    //Set CLOCK_DIV (OSC clock 1)
    SPI_Write_Command_Data(0x08,0x01);    //Set PRECHARGE_TIME_R (1 CLK)
    SPI_Write_Command_Data(0x09,0x01);    //Set PRECHARGE_TIME_G (1 CLK)
    SPI_Write_Command_Data(0x0A,0x01);    //Set PRECHARGE_TIME_B (1 CLK)
    SPI_Write_Command_Data(0x0B,0x0A);    //Set PRECHARGE_CURRENT_R
    SPI_Write_Command_Data(0x0C,0x0A);    //Set PRECHARGE_CURRENT_G
    SPI_Write_Command_Data(0x0D,0x0A);    //Set PRECHARGE_CURRENT_B
	SPI_Write_Command_Data(0x10,0x46);    //Set DRIVING_CURRENT_R
    SPI_Write_Command_Data(0x11,0x38);    //Set DRIVING_CURRENT_G
    SPI_Write_Command_Data(0x12,0x3A);    //Set DRIVING_CURRENT_B
	SPI_Write_Command_Data(0x13,0x00);    //Set DISPLAY_MODE_SET (normal)
    SPI_Write_Command_Data(0x14,0x31);    //Set RGB_IF (MPU mode)
    SPI_Write_Command_Data(0x15,0x00);    //Set RGB_POL
    SPI_Write_Command_Data(0x16,0x66);    //Set MEMORY_WRITE_MODE (16-bit mode)
    SPI_Write_Command_Data(0x17,0x00);    //Set MX1_ADDR
    SPI_Write_Command_Data(0x18,0x9F);    //Set MX2_ADDR
    SPI_Write_Command_Data(0x19,0x00);    //Set MY1_ADDR
    SPI_Write_Command_Data(0x1A,0x7F);    //Set MY2_ADDR
    SPI_Write_Command_Data(0x20,0x00);    //Set MEMORY_ACCESS_POINTER X
    SPI_Write_Command_Data(0x21,0x00);    //Set MEMORY_ACCESS_POINTER Y
    SPI_Write_Command_Data(0x28,0x7F);    //Set DUTY
    SPI_Write_Command_Data(0x29,0x00);    //Set DSL
    SPI_Write_Command_Data(0x2E,0x00);    //Set D1_DDRAM_FAC
    SPI_Write_Command_Data(0x2F,0x00);    //Set D1_DDRAM_FAR
    SPI_Write_Command_Data(0x31,0x00);    //Set D1_DDRAM_SAC
    SPI_Write_Command_Data(0x32,0x00);    //Set D1_DDRAM_SAR
    SPI_Write_Command_Data(0x33,0x00);    //Set SCR1_FX1 (0)
    SPI_Write_Command_Data(0x34,0x9F);    //Set SCR1_FX2 (159)
    SPI_Write_Command_Data(0x35,0x00);    //Set SCR1_FY1 (0)
    SPI_Write_Command_Data(0x36,0x7F);    //Set SCR1_FY2 (127)
    SPI_Write_Command_Data(0x80,0x00);    //Set IREF (controlled by external resistor)
    //Time_Sleep_MicroSeconds (10000);	  // Time to power up
    SPI_Write_Command_Data(0x06,0x01);    //Set DISP_ON_OFF (Display on)

	Clear();

	//PRINT_MSG("INIT DONE");

	return TRUE;
}

BOOL DD160128FC2A_Driver::Uninitialize()
{
	PRINT_MSG("UN-INIT DONE");
	Clear();
	PowerSave(FALSE);

	return TRUE;
}

void DD160128FC2A_Driver::PowerSave(BOOL On)
{
	PRINT_MSG("POWER SAVE");
	SPI_Write_Command_Data(0x06,On ? 0x01 : 0x00);    //Set DISP_ON_OFF(on)
}

void DD160128FC2A_Driver::Clear()
{
	int i;
	UINT16 color[32];

	for (i=0;i<32;i++)
		color[i] = 0;

	PRINT_MSG("CLEAR SCREEN");

	// reset pixel pos and set write mode
	SetPixelPos(0,0);

	for (i=0;i<g_DD160128FC2A_Driver.c_size_in_pixels;i+=32) {
		SendPixel(color,32);
	}

	//reset the cursor pos to the begining
	g_DD160128FC2A_Driver.m_cursor = 0;
}

void DD160128FC2A_Driver::BitBltEx(int xpos, int ypos, int width, int height, UINT32 data[])
{
	unsigned y;
	UINT16 *pixel = (UINT16*) data;

#if defined(CPU_UTILIZATION_MEASURE)
	INT64 GFX_HAL_Ticks_Start, GFX_HAL_Ticks_End;

	GFX_HAL_Ticks_Start = Time_CurrentTicks();
#endif

	ASSERT((xpos >= 0) && ((xpos+width) <= c_columns));
	ASSERT((ypos >= 0) && ((ypos+height) <= c_rows));

#if DEBUG
	hal_printf("BITBLTEX x=%d y=%d w=%d h=%d data=0x%lx\r\n",xpos,ypos,width,height,pixel);
	hal_printf("BITBLTEX col=%u row=%u\r\n",c_columns,c_rows);
#endif

	// write data directly to display memory
	for (y=ypos;y<ypos+height;y++) {
		// set x,y
		SetPixelPos(xpos,y);
		SendPixel(pixel,width);
		pixel = &pixel[width];
	}

#if defined(CPU_UTILIZATION_MEASURE)
	GFX_HAL_Ticks_End = Time_CurrentTicks();

	CPU_Utilization.GFX_HAL_Ticks += (GFX_HAL_Ticks_End - GFX_HAL_Ticks_Start);
#endif
}


void DD160128FC2A_Driver::BitBlt(int width, int height, int widthInWords, UINT32 data[], BOOL fUseDelta)
{
	_ASSERTE(width        == c_columns);
	_ASSERTE(height       == c_rows);
	_ASSERTE(widthInWords == width / c_pixel_in_word);

	PRINT_MSG("BITBLT");
#if defined(CPU_UTILIZATION_MEASURE)
	INT64 GFX_HAL_Ticks_Start, GFX_HAL_Ticks_End;

	GFX_HAL_Ticks_Start = Time_CurrentTicks();
#endif

	BitBltEx(0, 0, width, height, data);

#if defined(CPU_UTILIZATION_MEASURE)
	GFX_HAL_Ticks_End = Time_CurrentTicks();

	CPU_Utilization.GFX_HAL_Ticks += (GFX_HAL_Ticks_End - GFX_HAL_Ticks_Start);
#endif
}

//#include <Drivers\Display\font8x8.cpp>

// Macro for retriving pixel value in 1-bit bitmaps
#define DD160128FC2A_GETBIT(_x,_y,_data,_widthInWords) (((_data[((_x)/32) + (_y)*(_widthInWords)])>>((_x)%32)) & 0x1)

void DD160128FC2A_Driver::WriteString(char *str)
{
	PRINT_MSG("DD160128FC2A_Driver::WriteString");

	while (str && *str) {
		WriteFormattedChar(*str++);
	}
}

const unsigned char* ffont_8x12( unsigned int c );
const unsigned char* ffont_8x8( unsigned int c );

void DD160128FC2A_Driver::WriteChar(unsigned char c, int row, int col)
{
	UINT16 val[c_Font_Width];
	// convert to LCD pixel coordinates
	row *= c_Font_Height;
	col *= c_Font_Width;

	if (row > (c_rows - c_Font_Height)) return;
	if (col > (c_columns - c_Font_Width)) return;

	const UINT8* font = ffont_8x12(c);

	for (int y = 0 ; y < c_Font_Height; y++)
	{
		SetPixelPos(col,y+row);
		for (int x = 0; x < c_Font_Width; x++)
		{
			if ((font[y] >> (7-x)) & 0x1)
				val[x] = (UINT16) -1;
			else
				val[x] = 0;
		}
		// send the pixel
		SendPixel(val,c_Font_Width);
	}
}

void DD160128FC2A_Driver::WriteFormattedChar(unsigned char c)
{
	PRINT_HAL(c);

	if (c < 32)
	{
		switch (c)
		{
		case '\b':                      /* backspace, clear previous char and move cursor back */
			if ((g_DD160128FC2A_Driver.m_cursor % g_DD160128FC2A_Driver.c_text_columns) > 0)
			{
				g_DD160128FC2A_Driver.m_cursor--;
				WriteChar(' ', g_DD160128FC2A_Driver.m_cursor / g_DD160128FC2A_Driver.c_text_columns,
							    g_DD160128FC2A_Driver.m_cursor % g_DD160128FC2A_Driver.c_text_columns);
			}
			break;

		case '\f':                      /* formfeed, clear screen and home cursor */
			//Clear();
			g_DD160128FC2A_Driver.m_cursor = 0;
			break;

		case '\n':                      /* newline */
			g_DD160128FC2A_Driver.m_cursor += g_DD160128FC2A_Driver.c_text_columns;
			g_DD160128FC2A_Driver.m_cursor -= (g_DD160128FC2A_Driver.m_cursor % g_DD160128FC2A_Driver.c_text_columns);
			break;

		case '\r':                      /* carriage return */
			g_DD160128FC2A_Driver.m_cursor -= (g_DD160128FC2A_Driver.m_cursor % g_DD160128FC2A_Driver.c_text_columns);
			break;

		case '\t':                      /* horizontal tab */
			g_DD160128FC2A_Driver.m_cursor +=
				(c_Tab_Width - ((g_DD160128FC2A_Driver.m_cursor % g_DD160128FC2A_Driver.c_text_columns) % c_Tab_Width));

			// deal with line wrap scenario
			if ((g_DD160128FC2A_Driver.m_cursor % g_DD160128FC2A_Driver.c_text_columns) < c_Tab_Width)
			{
				// bring the cursor to start of line
				g_DD160128FC2A_Driver.m_cursor -= (g_DD160128FC2A_Driver.m_cursor % g_DD160128FC2A_Driver.c_text_columns);
			}
			break;

		case '\v':                      /* vertical tab */
			g_DD160128FC2A_Driver.m_cursor += g_DD160128FC2A_Driver.c_text_columns;
			break;

		default:
			DEBUG_TRACE2(TRACE_ALWAYS,
						 "Unrecognized control character in LCD_WriteFormattedChar: %2u (0x%02x)\r\n",
						 (unsigned int) c,
						 (unsigned int) c);
			break;
		}
	}
	else
	{
		WriteChar(c, g_DD160128FC2A_Driver.m_cursor / g_DD160128FC2A_Driver.c_text_columns,
				     g_DD160128FC2A_Driver.m_cursor % g_DD160128FC2A_Driver.c_text_columns);
		g_DD160128FC2A_Driver.m_cursor++;
	}

	if (g_DD160128FC2A_Driver.m_cursor >= (g_DD160128FC2A_Driver.c_text_columns * g_DD160128FC2A_Driver.c_text_rows))
	{
		g_DD160128FC2A_Driver.m_cursor -= g_DD160128FC2A_Driver.c_text_columns;
	}
}

INT32 DD160128FC2A_Driver::GetWidth()
{
    return c_columns;
}

INT32 DD160128FC2A_Driver::GetHeight()
{
    return c_rows;
}

INT32 DD160128FC2A_Driver::GetBitsPerPixel()
{
    return DD160128FC2A_BITS_PER_PIXEL;
}
/*
INT32 DD160128FC2A_Driver::GetPixelClockDivider()
{
    return 1;
}
 */
